package practiceProjects.textEncrypt;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.util.*;

/**
 * @Project ：learn_java
 * @File ：charRepeatEncrypt.java
 * @IDE ：IntelliJ IDEA
 * @Author ：自由如风间
 * @Date ：2025/3/21 19:45
 * @Description ：加密和解密文本
 * @ALGORITHM:
 * 有一个可打印字符的字符串，读取需要加密的文件，将字符替换为其在map里对应的int值，
 * 解密过程：在map中找值对应的键还原。
 * @Idea:
 * 可以根据文件的最后修改日期或者创建日期生成一个随机密钥（打乱过后的文本），
 * 再进行只属于此刻的加密和解密操作，这样就有99!种密钥，暴力完全破解不了！
 * 但此程序的所有操作都是针对英文文本的。
 */

public class charRepeatEncrypt {

    // 可打印的英文字符
    private static String printable =
            "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +
                    "!\"#$%&'()*+, -./:;<=>?@[\\]^_`{|}~ \n\t\r";

    private static HashMap<Character, Integer> map;
    // 静态变量需要先初始化
    static {
        map = new HashMap<>();
    }

    // 需要加密的文件地址
    private static String sourcePath = "G:\\codes\\learn_java\\src\\practiceProjects\\textEncrypt\\sourceText.txt";
    // 加密后的文件存储地址
    private static String encryptPath = "G:\\codes\\learn_java\\src\\practiceProjects\\textEncrypt\\encrypt.txt";
    // 解密后的文件存储地址
    private static String decryptPath = "G:\\codes\\learn_java\\src\\practiceProjects\\textEncrypt\\decrypt.txt";

    public static String getKey(HashMap map, String value){
        for(Object key: map.keySet()){
            if(map.get(key).equals(value)){
                return key.toString();
            }
        }
        return "";
    }

    /**
     * 生成map，键为可打印的字符，值为对应索引，索引将用于加密
     */
    private static void generateCharMap() {
        List<Character> characters = new ArrayList<>();
        for (char c : printable.toCharArray()) {
            characters.add(c);
        }

        // 根据初始文本的最后修改时间生成种子
        File sourceFile = new File(sourcePath);
        long seed = sourceFile.lastModified();
        Random random = new Random(seed);

        // 打乱字符列表的顺序
        Collections.shuffle(characters, random);

        printable = characters.toString();

        // 生成map
        int idx = 0;
        for (char i : printable.toCharArray()) {
            map.put(i, idx++);
        }
    }

    /**
     * 检查路径，不存在直接退出程序
     * @param f 文件
     */
    public static void checkPath(File f) {
        if (!f.exists()) {
            System.out.println("文件不存在，程序终止");
            System.exit(1);
        }
    }

    public static boolean decrypt(File f) throws IOException {
        checkPath(f);

        String content = new String(Files.readAllBytes(f.toPath()));
        if (content == null || content.isEmpty()) {
            System.out.println("需要解密文件为空，程序终止");
            System.exit(1);
        }

        StringBuilder sb = new StringBuilder();
        StringBuilder res = new StringBuilder();

        for (char i : content.toCharArray()) {
            if (i == ' ') {
                Object value = getKey(map, sb.toString());
                if (value == "") {
                    System.out.println("错误编码，解密失败，请检查文件完整或是否是正确文件");
                    System.exit(1);
                }
                res.append(value);
                sb = new StringBuilder();
            } else {
                sb.append(i);
            }
        }

        try (FileWriter fileWriter = new FileWriter(decryptPath)) {
            fileWriter.append(sb);
        }
        return true;
    }

    public static boolean encrypt(File f) throws IOException {
        checkPath(f);
        String content = new String(Files.readAllBytes(f.toPath()));

        StringBuilder sb = new StringBuilder();

        for (char i : content.toCharArray()) {
            if (!map.containsKey(i)) {
                System.out.println("不支持加密非英文文本！程序终止");
                // System.exit(1);
                return false;
            } else {
                // 将字符替换成其对应的索引
                sb.append(map.get(i).toString()).append(" ");
            }
        }

        /**
         * 将加密文本写入文件
         */
        try (FileWriter fileWriter = new FileWriter(encryptPath)) {
            fileWriter.append(sb);
        }

        return true;
    }

    public static void main(String[] args) {

        // 生成字符与对应索引map
        generateCharMap();

        // TODO 路径选择功能添加

        File file = new File(sourcePath);

        Scanner scanner = new Scanner(System.in);
        System.out.println("\t选择操作：\n1.加密\t2.解密");

        try {
            String i = scanner.nextLine();
            int it = Integer.parseInt(i);
            // 选择加密文件
            if (it == 1) {
                try {
                    boolean ok = encrypt(file);
                    if (ok) {
                        System.out.println("文件加密成功！已保存至" + encryptPath);
                    } else {
                        System.out.println("出现错误，文件加密失败");
                    }
                } catch (IOException e) {
                    System.out.println("加密过程中出现 IO 异常：" + e.getMessage());
                }
            // 解密文件
            } else if (it == 2) {
                boolean ok = decrypt(file);
                if (ok) {
                    System.out.println("文件解密成功！已保存至" + decryptPath);
                } else {
                    System.out.println("出现错误，文件解密失败");
                }
            } else {
                throw new IllegalArgumentException("请输入有效数字");
            }
        } catch (NumberFormatException e) {
            System.out.println("输入的不是有效的数字，请重新输入。");
        } catch (IllegalArgumentException e) {
            System.out.println(e.getMessage());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
